import { isString, isObject, isArray, isNull, isNumber, isFunction, isUndefined, isEmptyObject } from './type';
import { isPhone, isTel, isQQ, isHttp } from './regexp';
import { mainColor, AndroidPackageName, iosUrl, shareConf, uploadConf } from '@/config/index';
import shares from './share';
import http from "@/common/request.js";

// #ifdef APP-PLUS
import permision from './permission';
// #endif
// #ifdef H5
import h5Copy from "@/utils/h5copy";
// #endif

let _systemInfo = uni.getSystemInfoSync() || {};

let _id = 1;

/**
 * 设置本地储存
 * @param {String|{[key in string]: any}} arg1 要设置本地储存的键名，或储存对象
 * @param {any} arg2 要储存的值(arg1为对象时无效)
 * @example set(key, value)
 * @example set({key, value})
 * @example set({key1: value1, key2: value2})
 */
export function set(arg1, arg2) {
  if(isString(arg1) && arg2 !== undefined) {
    uni.setStorageSync(arg1, arg2);
  } else if(isObject(arg1)) {
    if(Object.keys(arg1).length == 2 && arg1.hasOwnProperty('key') &&  arg1.hasOwnProperty('value')) {
      let { key, value } = arg1;
      uni.setStorageSync(key, value);
    } else {
      for(let key in arg1) {
        let value = arg1[key];
        uni.setStorageSync(key, value);
      }
    }
  }
}

/**
 * 获取本地储存
 * @param {String|Array<String>} keys 
 * @returns {any|{[key in keys]: any}} 
 * @example get(key);
 * @example get([key1, key2]);
 */
export function get(keys) {
  if(!isArray(keys)) {
    return uni.getStorageSync(keys);
  } else {
    let values = {}
    for(let i = 0, l = keys.length; i < l; i++) {
      values[keys[i]] = uni.getStorageSync(keys[i]);
    }
    return values;
  }
}
	
/**
 * 删除本地储存
 * @param {Array<String>|String} keys 
 * @example del(key);
 * @example del([key1, key2]);
 */
export function del(keys) {
  if(!isArray(keys)) {
    uni.removeStorageSync(keys);
  } else {
    for(let i = 0, l = keys.length; i < l; i++) {
      uni.removeStorageSync(keys[i])
    }
  }
}

/**
 * 弹出提示信息
 * @param {String|{
 *  title: String;
 *  icon?: 'success' | 'loading' | 'none';
 *  image?: String;
 *  mask?: Boolean;
 *  duration?: Number;
 *  delay?: Number;
 *  position?: 'top' | 'center' | 'bottom';
 *  success?: Function;
 *  fail?: Function;
 *  complete?: Function;
 * }} args 标题或配置参数
 * @param {Boolean} isTitle 是否为标题
 * @prop {String} args.title 提示标题
 * @prop {'success' | 'loading' | 'none'} args.icon 提示图标，默认none
 * @prop {String} args.image 提示本地图片，默认未配置
 * @prop {Boolean} args.mask 是否遮罩，默认false
 * @prop {Number} args.duration 持续时长，默认1000
 * @prop {Number} args.delay 提示延时，默认200
 * @prop {'top' | 'center' | 'bottom'} args.position 提示位置，默认center
 * @prop {Function} args.success 成功回调（不建议使用，建议直接使用Promise处理）
 * @prop {Function} args.fail 失败回调（不建议使用，建议直接使用Promise处理）
 * @prop {Function} args.complete 完成回调（不建议使用，建议直接使用Promise处理）
 * @returns {Promise}
 * @example msg('操作成功！');
 * @example msg({title: '操作成功！', icon: 'success'});
 */
export function msg(args, isTitle = false) {
  return new Promise(async (resolve, reject) => {
    if(isTitle || !isObject(args)) args = { title: args };
    const { delay: delayTime = 0, ...ToastArgs } = args;
    const defConfig = {
      title: "标题", 
      icon: "none", 
      mask: false, 
      duration: 1000,
      // position: "center",
      success: (res) => {
        resolve(res);
      },
      fail: (err) => {
        reject(err);
      }
    }
    
    const conf = Object.assign({}, defConfig, args);
    const title = conf.title;
    if (isNumber(title) || isFunction(title)) conf.title = title.toString();
    else if (isNull(title)) conf.title = 'null';
    else if (isUndefined(title)) conf.title = 'undefined';
    else if (typeof title === 'object') conf.title = JSON.stringify(title);
    else conf.title = String(title);
    
    if(delayTime) await delay(delayTime);
    uni.hideToast();
    uni.showToast(conf);
  })
}

/**
 * modal弹窗
 * @param {String|{
 *  title?: String;
 *  content: String;
 *  showCancel?: Boolean;
 *  cancelText?: String;
 *  cancelColor?: HexColor;
 *  confirmText?: String;
 *  confirmColor?: HexColor;
 *  success?: Function;
 *  fail?: Function;
 *  complete?: Function;
 * }} args 内容或配置参数
 * @prop {String} args.title 提示的标题
 * @prop {String} args.content 提示的内容
 * @prop {Boolean} args.showCancel 是否显示取消按钮，默认为 true
 * @prop {String} args.cancelText 取消按钮的文字，默认为"取消"，最多 4 个字符
 * @prop {HexColor} args.cancelColor 取消按钮的文字颜色，默认为"#ff4a4a"
 * @prop {String} args.confirmText 确定按钮的文字，默认为"确定"，最多 4 个字符
 * @prop {HexColor} args.confirmColor 确定按钮的文字颜色，默认为config里配置的主色
 * @prop {Function} args.success 成功回调（不建议使用，建议直接使用Promise处理）
 * @prop {Function} args.fail 失败回调（不建议使用，建议直接使用Promise处理）
 * @prop {Function} args.complete 完成回调（不建议使用，建议直接使用Promise处理）
 * @returns {Promise}
 * @example await confirm('确定要删除吗！');
 * @example await confirm({title: '警告', content: '确定要删除吗？'});
 */
export function confirm(args) {
  return new Promise((resolve, reject) => {
    if(!isObject(args)) args = { content: String(args) };
    const defConfig = {
      title: "提示", 
      content: "提示内容",
      showCancel: true,
      cancelText: '取消',
      confirmText: '确认',
      confirmColor: mainColor,
      success: (res) => {
        if(res.confirm) {
          resolve('点击了确认');
        } else if(res.cancel) {
          reject('点击了取消！');
        } else {
          reject(res)
        }
      },
      fail: (err) => {
        reject(err);
      }
    }
    uni.showModal(Object.assign({}, defConfig, args));
  });
}

/**
 * 显示加载中
 * @param {String} title 提示的文字内容，显示在loading的下方
 * @param {Boolean} mask 是否显示透明蒙层，防止触摸穿透，默认：false
 */
export function showLoading(args) {
  if(isString(args)) args = { title: args }; 
  // #ifdef H5
  const defConf = { 
    title: "加载中...", 
    mask: false, 
    image: '/static/loading/loading2.gif', 
    duration: 99999999 ,
  };  
  msg({ ...defConf, ...args });
  // #endif
  // #ifndef H5
  const defConf = {
    title: "加载中...", 
    mask: false,
  };  
  uni.showLoading({ ...defConf, ...args });
  // #endif
}

/**
 * 隐藏loading
 */
export function hideLoading() {
  // #ifdef H5
  uni.hideToast();
  // #endif
  // #ifndef H5
  uni.hideLoading();
  // #endif
}

/**
 * 拷贝文本(不是字符串将转换为字符串)
 * @param {any} content 要拷贝的文本
 * @param {Boolean} showTosat 是否显示提示
 * @return {Promise}
 * @example copyText('休息休息');
 * @example copyText({a: 1, b: 2});
 */
export function copyText(content, showTosat = true) {
  let text = '';
  if (isString(content)) text = content;
  else if (isNumber(content) || isFunction(content)) text = content.toString();
  else if (isNull(content)) text = 'null';
  else if (isUndefined(content)) text = 'undefined';
  else if (typeof args === 'object') text = JSON.stringify(content);
  else text = String(content);
  return new Promise((resolve, reject) => {
    // #ifdef H5
    const result = h5Copy(text);
    if (result === false) {
      if(showTosat) msg({title: '复制失败！', icon: 'none'});
      reject("复制失败！");
    } else {
      if(showTosat) msg({title: '内容已复制', icon: 'success'});
      resolve("内容已复制");
    }
    // #endif
    // #ifndef H5
    uni.setClipboardData({
      data: text,
      success: (res) => {
        if(!showTosat) uni.hideToast();;
        resolve(res);
      },
      fail: (err) => {
        if(!showTosat) uni.hideToast();;
        reject(err);
      }
    });
    // #endif
  })
}

/**
 * 获取拷贝文本
 * @param {Boolean} 是否尝试json转换  
 * @return {Promise}
 * @example getCopyText();
 * @example getCopyText(true);
 */
export function getCopyText(toJson = false) {
  return new Promise((resolve, reject) => {
    // #ifndef H5
    uni.getClipboardData({
      success: (res) => {
        if(toJson) {
          try {
            res.data = JSON.parse(res.data);
          }catch(err) {}
        }
        resolve(res.data);
      },
      fail: (err) => {
        reject(err);
      }
    });
    // #endif
    // #ifdef H5
    reject('浏览器不支持');
    // #endif
  })
}

/**
 * 获取随机字符串
 * @param {Number} min 最小长度
 * @param {Number} max 最大长度
 * @returns {String} 随机字符串
 * @example ranStr(1, 10);
 */
export function ranStr(min = 1, max = 10) {
  if(!isNumber(min)) min = 1;
  if(!isNumber(max)) max = 10;
  if(min < 0) min = 0;
  if(max < min) return '';
  let len = min + Math.floor(Math.random() * (max - min));
  let str = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ0123456789";
  let s = "";
  for (let i = 0; i < len; i++) {
    s += str.charAt(Math.floor(Math.random() * str.length));
  }
  return s;
}

/**
 * 获取格式化的日期
 * @param {Date|Number|String} arg1 要格式化的日期，不传则使用当前日期
 * @param {String} arg2 分隔符
 * @returns 格式化后的日期
 * @example getDate();
 * @example getDate(Date.now() - 7 * 24 * 3600 * 1000);
 * @example getDate(null, '/');
 */
export function getDate(arg1, arg2 = '-') {
  let d = arg1 ? new Date(arg1) : new Date();
  let y = d.getFullYear();
  let m = (d.getMonth() + 1).toString().padStart(2, 0);
  let day = d.getDate().toString().padStart(2, 0);
  return y + arg2 + m + arg2 + day;
}

/**
 * 获取格式化的时间
 * @param {Date|Number|String} arg1 要格式化的时间，不传则使用当前时间
 * @param {String} arg2 分隔符
 * @returns 格式化后的时间
 * @example getTime();
 * @example getTime(Date.now() - 2 * 3600 * 1000);
 * @example getTime(null, '-');
 */
export function getTime(arg1, arg2 = ':') {
  let d = arg1 ? new Date(arg1) : new Date();
  let h = d.getHours().toString().padStart(2, 0);
  let m = d.getMinutes().toString().padStart(2, 0);
  let s = d.getSeconds().toString().padStart(2, 0);
  return h + arg2 + m + arg2 + s;
}

/**
 * 获取格式化的日期+时间
 * @param {Date|Number|String} arg1 要格式化的日期+时间，不传则使用当前日期+时间
 * @param {String} arg2 日期分隔符
 * @param {String} arg3 时间分隔符
 * @returns 格式化后的日期+时间
 * @example getDateTime();
 */
export function getDateTime(arg1, arg2 = '-', arg3 = ':') {
  return getDate(arg1, arg2) + " " + getTime(arg1, arg3);
}

/**
 * 数字处理
 * @param {Number} num 要处理的数字
 * @returns 处理后的数字
 * @example handleNum(123456789);
 */
export function handleNum(num) {
  if(!isNumber(num)) num = Number(num);
  if(isNaN(num)) return 0;
  let mark = '';
  if(num < 0) {
    mark = '-';
    num = -num;
  }
  if (num < 10000) {
    return mark + num;
  } else if (num < 100000000) {
    let n = Math.round((num / 10000) * 100) / 100;
    n = parseFloat(n.toString().slice(0, 5));
    return mark + n + "万";
  } else {
    let n = Math.round((num / 100000000) * 100) / 100;
    n = parseFloat(n.toString().slice(0, 5));
    return mark + n + "亿";
  }
}

/**
 * 去除字符串便签，并截取长度
 * @param {String} html 要去除标签的字符串
 * @param {Number} max 要截取的长度
 * @returns 处理后的字符串
 * @example removeTag('<p>123</p>');
 * @example removeTag('<p>123</p>', 2);
 */
export function removeTag(html, max) {
  let text = html.replace(/(<\/?[^>]+>)|(\s)/g, "");
  if(isNumber(max) && max > 0) text = text.slice(0, max);
  return text;
}


// #ifdef APP-PLUS
/**
 * 验证权限
 * @returns {Promise}
 */
async function checkPermission() {
  let status = permision.isIOS
    ? await permision.requestIOS("camera")
    : await permision.requestAndroid("android.permission.CAMERA");
  return new Promise((resolve, reject) => {
    if (status === null || status === 1) {
      resolve(1);
    } else {
      uni.showModal({
        content: "需要相机权限",
        confirmText: "设置",
        success: function (res) {
          if (res.confirm) {
            permision.gotoAppSetting();
          }
        },
      });
      resolve(0);
    }
  })
}
// #endif

/**
 * 扫码
 * @returns {Promise}
 */
export async function scan() {
  // #ifdef APP-PLUS
  let status = await checkPermission();
  if (status !== 1) {
    msg("手机不支持扫码！");
    reject("手机不支持扫码！");
    return;
  }
  // #endif
  return new Promise((resolve, reject) => {
    // #ifndef H5
    uni.scanCode({
      success: (res) => {
        resolve(res.result);
      },
      fail: (err) => {
        // #ifdef MP
        uni.getSetting({
          success: (res) => {
            let authStatus = res.authSetting["scope.camera"];
            if (!authStatus) {
              uni.showModal({
                title: "授权失败",
                content: "需要你开启相机权限进行扫码，请在设置界面打开相关权限",
                success: (res) => {
                  if (res.confirm) {
                    uni.openSetting();
                  }
                },
              });
            }
          },
        });
        // #endif
        reject(err);
      },
    });
    // #endif

    // #ifdef H5
    msg("网页不支持扫码！");
    reject("网页不支持扫码！");
    // #endif
  });
}

/**
 * 打电话
 * @param {String|Number} tel 电话号码
 */
export function tel(tel = 10086) {
  return new Promise((resolve, reject) => {
    uni.makePhoneCall({
      phoneNumber: String(tel), 
      success: (res) => {
        console.log(res)
        resolve(res)	
      },
      fail: (err) => {
        console.log(err)
        reject(err)
      }
    });
  });
}
		
/**
 * 打开qq聊天框
 * @param {Number|String} qq qq号
 * @example qq(123456)
 */
export function qq(qq = 123456) {
  if(!isQQ(qq)) throw '请传入QQ号！';
  // #ifdef H5
  window.location.href = /Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent) ? "mqqwpa://im/chat?chat_type=wpa&uin="+qq+"&version=1&src_type=web&web_src=bjhuli.com" : "http://wpa.qq.com/msgrd?v=3&uin="+qq+"&site=qq&menu=yes";
  // #endif
  // #ifdef APP-PLUS
  plus.runtime.openURL('mqq://im/chat?chat_type=wpa&uin=' + qq + '&version=1&src_type=web');
  // #endif
}

/**
 * 选择图片
 * @param {Number|{
 *  count: Number;
 *  sizeType?: Array<String>;
 *  sourceType?: Array<String>;
 *  extension?: Array<String>;
 *  success?: Function;
 *  fail?: Function;
 *  complete?: Function;
 * }} args 个数或配置参数
 * @prop {Number} args.count 最多可以选择的图片张数，默认1
 * @prop {Array<String>} args.sizeType original 原图，compressed 压缩图，默认二者都有
 * @prop {Array<String>} args.sourceType album 从相册选图，camera 使用相机，默认二者都有。如需直接开相机或直接选相册，请只使用一个选项。（h5无效）
 * @prop {Array<String>} args.extension 根据文件拓展名过滤，每一项都不能是空字符串。默认不过滤。
 * @prop {Function} args.success 成功回调（不建议使用，建议直接使用Promise处理）
 * @prop {Function} args.fail 失败回调（不建议使用，建议直接使用Promise处理）
 * @prop {Function} args.complete 完成回调（不建议使用，建议直接使用Promise处理）
 * @returns {Promise}
 * @example await selectImg(3);
 * @example await selectImg({count: 1, sizeType: ['compressed']});
 */
export function selectImg(args = 1) {
  return new Promise((resolve, reject) => {
    if(!isObject(args) && isNaN(Number(args))) throw '请传入选择图片个数或配置对象';
    args = { count: Number(args) };
    const defConfig = {
      count: 1,
      sizeType: ['original', 'compressed'],
      sourceType: ['album', 'camera'],
      // extension: ['png', 'jpg', 'gif', 'jpeg', 'ico'],
      success: (res) => {
        resolve(res);
      },
      fail: (err) => {
        reject(err);
      }
    }
    uni.chooseImage(Object.assign({}, defConfig, args));
  });
}

/**
 * 打开应用市场
 * @param {String} marketPackageName 商店的包名
 * @example openMarket(); // 打开系统自带应用市场
 * @example openMarket('com.tencent.android.qqdownloader') // 强制打开应用宝
 */
export function openMarket(marketPackageName){
  // #ifdef APP-PLUS
  var appUrl;
  if (plus.os.name=="Android") {
    appUrl = "market://details?id="+AndroidPackageName;
  } else{
    appUrl = "itms-apps://"+iosUrl;
  }
  if (isUndefined(marketPackageName)) {
    plus.runtime.openURL(appUrl, function(err) {
      msg("打开应用商店失败！");
    });
  } else{ 
    if (plus.os.name=="Android") {
      plus.runtime.openURL(appUrl, function(err) {
        msg("打开应用商店失败！");
      }, marketPackageName);
    } else{
      msg("仅Android手机才支持应用宝");
    }
  }
  // #endif
  
  // #ifdef H5
  msg('网页不支持打开应用商店');
  // #endif
}

/**
 * 调用系统分享
 * @param {String} text 分享的文字
 */
export function sysShare(text = '分享内容') {
  return new Promise((resolve, reject) => {
    // #ifdef APP-PLUS
    plus.share.sendWithSystem(
      {
        type: "text",
        content: text,
      },
      resolve,
      reject,
    );
    // #endif
    // #ifndef APP-PLUS
    msg('平台不支持打开系统分享');
    // #endif
  });
}

/**
 * 自定义分享
 * @param {*} shareInfo 分享对象，在config里可以配置默认值
 * @param {*} shareApps 支持的类型 默认：[微信、朋友圈、微博、qq、复制、系统分享]
 * @returns {ShareObj} 分享对象，用于关闭分享弹窗
 * @example const shareObj = share(); shareObj.alphaBg.hide(); shareObj.shareMenu.hide();
 */
export function share(shareInfo = {}, shareApps = ['wx', 'pyq', 'wb', 'qq', 'copy', 'more']) {
  shareInfo = Object.assign({}, shareConf, shareInfo);
  const shareTypes = {
    wx: {
      icon: "/static/sharemenu/wx.png",
      text: "微信好友",
    },
    pyq: {
      icon: "/static/sharemenu/pyq.png",
      text: "朋友圈"
    },
    wb: {
      icon: "/static/sharemenu/weibo.png",
      text: "微博"
    },
    qq: {
      icon: "/static/sharemenu/qq.png",
      text: "QQ"
    },
    copy: {
      icon: "/static/sharemenu/copy.png",
      text: "复制"
    },
    more: {
      icon: "/static/sharemenu/more.png",
      text: "更多"
    },
  }
  let shareList = shareApps.map(item => shareTypes[item]);
  let shareObj;
  const promise = new Promise((resolve, reject) => {
    shareObj = shares(shareInfo, shareList, function(index) {
      console.log("点击按钮的序号: " + index);
      let shareConf = {
        href: shareInfo.href || "",
        title: shareInfo.title || "",
        summary: shareInfo.desc || "",
        success: (res) => {
          resolve(res);
        },
        fail: (err) => {
          reject(err);
        }
      };
      const funs = {
        wx: () => {
          shareConf.provider = "weixin";
          shareConf.scene = "WXSceneSession";
          shareConf.type = 0;
          shareConf.imageUrl = shareInfo.imgUrl || "";
          uni.share(shareConf);
        },
        pyq: () => {
          shareConf.provider = "weixin";
          shareConf.scene = "WXSenceTimeline";
          shareConf.type = 0;
          shareConf.imageUrl = shareInfo.imgUrl || "";
          uni.share(shareConf);
        },
        wb: () => {
          shareConf.provider = "sinaweibo";
          shareConf.type = 0;
          shareConf.imageUrl = shareInfo.imgUrl || "";
          uni.share(shareConf);
        },
        qq: () => {
          shareConf.provider = "qq";
          shareConf.type = 1;
          shareConf.imageUrl = shareInfo.imgUrl || "";
          uni.share(shareConf);
        },
        copy: () => {
          copyText(shareInfo.href, true); 
        },
        more: () => {
          plus.share.sendWithSystem({
            type: "web",
            title: shareInfo.title || "",
            thumbs: [shareInfo.imgUrl || ""],
            href: shareInfo.href || "",
            content: shareInfo.desc || "",
          });
        },
      }
      if(isFunction(funs[shareApps[index]])) funs[shareApps[index]]();
    });
    setTimeout(() => {
      shareObj.alphaBg.show();
      shareObj.shareMenu.show();
    }, 100);
  });
  promise.hide = () => {
    shareObj.alphaBg.hide();
    shareObj.shareMenu.hide();
  };
  return promise;
}

/**
 * 放大查看图片
 * @param {String|{
 *  urls: Array<String>;
 *  current?: Number|String;
 *  indicator?: 'default' | 'number' | 'none';
 *  loop?: Boolean;
 *  longPressActions?: {
 *    itemList: Array<String>;
 *    itemColor?: HexColor;
 *    success?: Function;
 *    fail?: Function;
 *    complete?: Function;
 *  };
 *  success?: Function;
 *  fail?: Function;
 *  complete?: Function;
 * }} args 图片地址 或 图片地址数组 或 配置参数
 * @prop {Array<String>} args.urls 需要预览的图片链接列表
 * @prop {Number|String} args.current 当前位置 下标索引或链接
 * @prop {'default' | 'number' | 'none'} args.indicator 图片指示器样式，可取值："default" - 底部圆点指示器； "number" - 顶部数字指示器； "none" - 不显示指示器。
 * @prop {Boolean} args.loop 是否可循环预览，默认值为 false
 * @prop {Object} args.longPressActions 长按图片显示操作菜单，如不填默认为保存相册
 * @prop {Array<String>} args.longPressActions.itemList 按钮的文字数组
 * @prop {HexColor} args.longPressActions.itemColor 按钮的文字颜色，字符串格式，默认为"#000000"
 * @prop {Function} args.longPressActions.success 成功回调
 * @prop {Function} args.longPressActions.fail 失败回调
 * @prop {Function} args.longPressActions.complete 完成回调
 * @prop {Function} args.success 成功回调（不建议使用，建议直接使用Promise处理）
 * @prop {Function} args.fail 失败回调（不建议使用，建议直接使用Promise处理）
 * @prop {Function} args.complete 完成回调（不建议使用，建议直接使用Promise处理）
 * @param {String|Number} current 当前位置 下标索引或链接
 * @returns {Promise}
 * @example lookImg('http://xxx.com/123.png');
 * @example lookImg({ urls, current: 3, longPressActions: { itemList: ['识别二维码'], success(res) {console.log(res.tapIndex)} } });
 */
export function lookImg(args, current = 0) {
  return new Promise((resolve, reject) => {
    if(!isString(args) && !isArray(args) && !isObject(args)) throw '请传入图片地址、或图片地址数组、或配置对象';
    if (isString(args)) args = { urls: [args], current };
    else if(isArray(args)) args = { urls: args, current };
    const defConfig = {
      urls: [],
      success: (res) => {
        resolve(res);
      },
      fail: (err) => {
        reject(err);
      },
    }
    uni.previewImage(Object.assign({}, defConfig, args));
  });
}

/**
 * 下载文件
 * @param {String|{
 *  url: String;
 *  header?: Object;
 *  timeout?: Number;
 *  success?: Function;
 *  fail?: Function;
 *  complete?: Function;
 * }} args 下载地址或配置参数
 * @prop {String} args.url 下载资源的 url
 * @prop {Object} args.header HTTP 请求 Header, header 中不能设置 Referer。
 * @prop {Number} args.timeout 超时时间，单位 ms
 * @prop {Function} args.success 成功回调（不建议使用，建议直接使用Promise处理）
 * @prop {Function} args.fail 失败回调（不建议使用，建议直接使用Promise处理）
 * @prop {Function} args.complete 完成回调（不建议使用，建议直接使用Promise处理）
 * @param {Function} callback 回调函数，传递下载对象，用于取消下载和下载速度监听。
 * @returns {Promise}
 * @example download('http://xxx.com/123.png');
 * @example download({ url, timeout: 10000 });
 */
export function download(args, callback) {
  return new Promise((resolve, reject) => {
    if(!isString(args)&& !isObject(args)) throw '请传入下载地址或配置对象';
    if (isString(args)) args = { url: args };
    const defConfig = {
      url: '',
      success: (res) => {
        resolve(res.tempFilePath);
      },
      fail: (err) => {
        reject(err);
      },
    }
    const downloadTask = uni.downloadFile(Object.assign({}, defConfig, args));
    if(isFunction(callback)) callback(downloadTask);
  });
}

/**
 * 上传文件
 * @param {Object|String} args 上传文件资源的路径或配置参数
 * @returns {Promise}
 * @example upload(res.tempFilePaths[0]);
 * @example upload({ filePath, name: 'file1', formData: { type: 1 } });
 */
export function uploadFile(args) {
  if(!isString(args)&& !isObject(args)) throw '请传入上传文件路径或配置对象';
  if (isString(args)) args = { filePath: args };
  const defConfig = {
    url: uploadConf.fileApi,
    name: uploadConf.name,
  }
  return http.upload(Object.assign({}, defConfig, args));
}

/**
 * 上传图片
 * @param {String|Object} args 上传文件资源的路径或配置参数
 * @returns {Promise}
 */
export function uploadImg(args) {
  if(isString(args)) return uploadFile({ url: uploadConf.imgApi, filePath: args });
  if(isObject(args)) return uploadFile({ url: uploadConf.imgApi, ...args });
  throw '请传入上传文件路径或配置对象';
}

/**
 * 上传视频
 * @param {String|Object} args 上传文件资源的路径或配置参数
 * @returns {Promise}
 */
export function uploadVideo(args) {
  if(isString(args)) return uploadFile({ url: uploadConf.videoApi, filePath: args });
  if(isObject(args)) return uploadFile({ url: uploadConf.videoApi, ...args });
  throw '请传入上传文件路径或配置对象';
}

/**
 * 扫描图片二维码|条形码
 * @param {String} url 图片地址，支持网路图片和本地图片（网络图片会先下载再识别）
 * @param {Array<Number>} filters 支持的类型 0-15 默认 [0, 1, 2]
 * @param {Boolean} chatSet 自动解码字符集 默认 false
 */
export function scanImg(url, filters = [0, 1, 2], chatSet = false) {
  return new Promise(async (resolve, reject) => {
    showLoading('扫描中...');  
    if(!isString(url)) throw '请传入要扫描的图片地址！';
    if(isHttp(url)) { 
      try {
        url = await download(url);
      } catch (err) {
        reject(err);
        hideLoading();
      } 
    }
    plus.barcode.scan(url, (type, code, file, charset) => {
      hideLoading(); 
      resolve(code)
    }, (err) => {
      hideLoading();
      reject(err);
    }, filters, chatSet);
  });
}

/**
 * 获取当前页面
 */
export function getCurrentPage() {
  try {
    return this.$mp.page;
  } catch(e) {
    let routes = getCurrentPages();
    return routes[routes.length - 1];
  }
}

/**
 * 获取当前页面路由字符串
 */
export function getCurrentRoute() {
  return (getCurrentPage() || {}).route;
}

/**
 * 延时执行
 * @param {} time 延时时间
 * @returns {Promise}
 */
export function delay(time = 500) {
  let setT;
  const promise = new Promise((resolve) => {
    setT = setTimeout(() => {
      resolve(true)
    }, Number(time) || 500);
  });
  promise.abort = () => {
    clearTimeout(setT);
    return Promise.resolve(false);
  };
  return promise;
}

/**
 * 获取位置信息
 */
export function getLocation(key) {
  return new Promise((resolve, reject) => {
    uni.getLocation({
      geocode: true,
      success(res) {
        const addressKeys = Object.keys(res.address || {});
        if(!key) {
          resolve(res);
        } else {
          if(addressKeys.indexOf(key) === -1) {
            resolve(res[key]);
          } else {
            resolve(res.address[key]);
          }
        }
      },
      fail(err) {
        reject(err);
      }
    })
  })
}

/**
 * 获取系统信息
 * @param {*} key 要获取的系统信息，不传返回全部
 */
export function getSystemInfo(key) {
  return key ? _systemInfo[key] : _systemInfo;
}

/**
 * 获取唯一id
 */
export function getId() {
  return ++_id;
}